package org.lep.leetcode.besttimetobuyandsellstock;

/**
 * Source : https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/
 *
 * Created by lverpeng on 2017/8/21.
 *
 * Say you have an array for which the ith element is the price of a given stock on day i.
 *
 * Design an algorithm to find the maximum profit. You may complete at most two transactions.
 *
 * Note:
 * You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
 */
public class BestTimeToBuyAndSellStock3 {


    /**
     * 求出最大利润，本题目限制了买卖次数为2，同一时刻只能持有一只股票
     * 因为不能同时持有一支以上股票，所以两次买卖的时间不会重叠，所以可以把prices分割为两段，求出每段的最大值，然后将两个最大值相加得到最大利润
     * 但是不确定怎么分割prices，所以要尝试每一种分割方法，以i为分割点pricesp[0:n] = prices[0:i] + prices[i:n]
     * 求出每种分割情况下两段的最大值，针对每一种分割后的情况使用stcok1中的解法
     *
     * 这里先使用循环实现
     *
     * @param prices
     * @return
     */
    public int maxProfit (int[] prices) {
        if (prices.length <=1 ) {
            return 0;
        }
        int maxProfit = Integer.MIN_VALUE;
        for (int i = 1; i < prices.length-1; i++) {
            int min = prices[0];
            int max = Integer.MIN_VALUE;
            for (int j = 0; j <= i; j++) {
                if (min > prices[j]) {
                    min = prices[j];
                } else {
                    int profit = prices[j] - min;
                    max = profit > max ? profit : max;
                }
            }

            int max2 = Integer.MIN_VALUE;
            min = prices[i];
            for (int j = i; j < prices.length; j++) {
                if (min > prices[j]) {
                    min = prices[j];
                } else {
                    int profit = prices[j] - min;
                    max2 = profit > max2 ? profit : max2;
                }
            }
            max = max2 + max;
            maxProfit = max > maxProfit ? max : maxProfit;

        }
        return maxProfit;
    }

    /**
     * 使用DP来优化时间复杂度，上面时间复度是O(n^2)
     * 正向循环先计算出分割后的第一个部分的最大值，i从1:n-2分割后的第一部分的最大值存储在数组中
     * 然后逆向循环计算分割后第二部分的最大值，再与该位置第一部分计算得出的最大值相加就是该种分割情况下的最大值
     * 循环完成后扫描结果数组中的最大值就是所要求的最大利润
     *
     * @param prices
     * @return
     */
    public int maxProfitByDP (int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int[] maxArr = new int[prices.length];
        int max = 0;
        int min = prices[0];
        for (int i = 0; i < prices.length; i++) {
            if (prices[i] < min) {
                min = prices[i];
            } else {
                int profit = prices[i] - min;
                max = profit < max ? max : profit;
            }
            maxArr[i] = max;
        }

        int result = Integer.MIN_VALUE;
        max = 0;
        int maxRightProfit = 0;
        for (int i = prices.length-1; i > 0; i--) {
            if (prices[i] > max) {
                max = prices[i];
            } else {
                int profit = max - prices[i];
                maxRightProfit = profit > maxRightProfit ? profit : maxRightProfit;
            }
            result = Math.max(result, maxRightProfit + maxArr[i]);
        }
        return result;
    }

    public static void main(String[] args) {
        BestTimeToBuyAndSellStock3 bestTimeToBuyAndSellStock3 = new BestTimeToBuyAndSellStock3();
        System.out.println(bestTimeToBuyAndSellStock3.maxProfit(new int[]{1,2,3,5,-1,4,7}));
        System.out.println(bestTimeToBuyAndSellStock3.maxProfitByDP(new int[]{1,2,3,5,-1,4,7}));
    }
}
